package com.webonn.frame.db;

import com.google.common.collect.Lists;
import com.google.gson.Gson;

import com.webonn.frame.core.Pagination;
import org.hibernate.*;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.transform.Transformers;
import org.hibernate.type.StandardBasicTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManagerFactory;
import java.io.Serializable;
import java.util.*;

/**
 * Created by Administrator on 2016/6/15.
 */
@Repository
public class MyDao {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyDao.class);

    private SessionFactory sessionFactory;

    @Autowired
    public MyDao(EntityManagerFactory factory) {
        if(factory.unwrap(SessionFactory.class) == null){
            throw new NullPointerException("factory is not a hibernate factory");
        }
        this.sessionFactory = factory.unwrap(SessionFactory.class);
    }

  /*  @Autowired
    public MyDao(SessionFactory sessionFactory) {
        this.sessionFactory =  Objects.requireNonNull(sessionFactory);
    }*/

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public Session openSession(){
        return this.sessionFactory.openSession();
    }

    public List<Map<String, Object>> queryResultToMap(String sql, Object [] objects, String ... booleanKeys){
        Session session = this.sessionFactory.openSession();
        try {
            SQLQuery query = bindSQLQueryParams(session.createSQLQuery(sql), objects );
            for(String key:booleanKeys){
                query.addScalar(key, StandardBasicTypes.BOOLEAN);
            }
            List<Map<String, Object>> result = query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
            return result;
        }catch(Exception e){
            LOGGER.error(String.format("query [%s] for map error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public boolean executeSQLUpdate(String sql) {
        Session session = this.sessionFactory.openSession();
        try {
            Transaction tx = session.beginTransaction();
            SQLQuery q = session.createSQLQuery(sql);
            tx.commit();
            return q.executeUpdate() >= 0 ;
        }catch(Exception e){
            LOGGER.error(String.format("execute [%s]  error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return false;
    }


    public SQLQuery bindSQLQueryParams(SQLQuery sqlQuery, Object [] objects){
        if(Objects.isNull(objects) || objects.length < 1)
            return sqlQuery;
        for(int i=0; i< objects.length; i++ ){
            if(objects[i] instanceof Date){
                sqlQuery.setDate(i, (Date)objects[i]);
            }else if(objects[i] instanceof Float){
                sqlQuery.setFloat(i, (Float)objects[i]);
            }else if(objects[i] instanceof Double){
                sqlQuery.setDouble(i, (Double)objects[i]);
            }else if(objects[i] instanceof Integer){
                sqlQuery.setInteger(i, (Integer)objects[i]);
            }else if(objects[i] instanceof Long){
                sqlQuery.setLong(i, (Long)objects[i]);
            }else{
                sqlQuery.setParameter(i, objects[i]);
            }
        }
        return sqlQuery;
    }

    public <T> Pagination<T> listPagination(String sql, Pagination pagination , Class<T> clazz, Object [] objects) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            SQLQuery sqlQuery = session.createSQLQuery(sql);
            int total = bindSQLQueryParams(sqlQuery,objects ).addEntity(clazz).list().size();
            String limit_sql = String.format(" limit %d , %d", pagination.getStart(), pagination.getLimit() );
            List<T> rows = bindSQLQueryParams(session.createSQLQuery(sql + limit_sql),objects).addEntity(clazz).list();
            pagination.setTotal(total);
            pagination.setRows(rows == null ? Lists.newArrayList():rows);
            return pagination;
        }catch(Exception e){
            LOGGER.error(String.format("query sql [%s]  for list error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public <T> List<T> list(String sql, Class<T> clazz, Object [] objects) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            SQLQuery sqlQuery = session.createSQLQuery(sql);
            return bindSQLQueryParams(sqlQuery, objects).addEntity(clazz).list();
        }catch(Exception e){
            LOGGER.error(String.format("query sql [%s]  for list error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public <T> T uniqueResult(String sql, Class<T> clazz, Object [] objects) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            List<T> results = list(sql, clazz, objects);
            if(results !=null && results.size()>0){
                return results.get(0);
            }
        }catch(Exception e){
            LOGGER.error(String.format("query  [%s]error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public <T> List<T> listDTO(String sql, Class<T> clazz, Object [] objects) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            return bindSQLQueryParams(session.createSQLQuery(sql), objects).setResultTransformer(Transformers.aliasToBean(clazz)).list();
        }catch(Exception e){
            LOGGER.error(String.format("query sql [%s]  for list error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public List<String> listIds(String sql, Object [] objects) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            return (List<String>)bindSQLQueryParams(session.createSQLQuery(sql), objects).list();
        }catch(Exception e){
            LOGGER.error(String.format("query sql [%s]  for list error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public List<Object> list(String sql, Object [] objects) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            return bindSQLQueryParams(session.createSQLQuery(sql), objects).list();
        }catch(Exception e){
            LOGGER.error(String.format("query sql [%s]  for list error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public List<LinkedHashMap<String, Object>> listObjects(String sql, Object [] objects){
        Session session = this.sessionFactory.openSession();
        try {
            List<LinkedHashMap<String, Object>> result = bindSQLQueryParams(session.createSQLQuery(sql), objects).setResultTransformer(new ResultTransformer() {

                @Override
                public Object transformTuple(Object[] values, String[] columns) {
                    Map<String, Object> map = new LinkedHashMap<String, Object>(1);
                    int i = 0;
                    for(String column : columns){
                        map.put(column, values[i++]);
                    }
                    return map;
                }

                @Override
                public List transformList(List list) {
                    return list;
                }
            }).list();
            return result;
        }catch(Exception e){
            LOGGER.error(String.format("query [%s] for map error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public List<Map<String, Object>> queryResultToMap(String sql, Object [] objects){
        Session session = this.sessionFactory.openSession();
        try {
            List<Map<String, Object>> result = bindSQLQueryParams(session.createSQLQuery(sql), objects).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
            return result;
        }catch(Exception e){
            LOGGER.error(String.format("query [%s] for map error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public <T> Pagination<T> queryResultToMapPagination(String sql, Pagination pagination, Object [] objects) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            int total = bindSQLQueryParams(session.createSQLQuery(sql), objects).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list().size();
            String limit_sql = String.format(" limit %d , %d", pagination.getStart(), pagination.getLimit() );
            List<T> rows = bindSQLQueryParams(session.createSQLQuery(sql + limit_sql), objects).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
            pagination.setTotal(total);
            pagination.setRows(rows == null ? Lists.newArrayList():rows);
            return pagination;
        }catch(Exception e){
            LOGGER.error(String.format("query sql [%s]  for list error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public <T> Pagination<T> queryResultToMapPagination(String sql, Pagination pagination, Object [] objects, int total) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            String limit_sql = String.format(" limit %d , %d", pagination.getStart(), pagination.getLimit() );
            List<T> rows = bindSQLQueryParams(session.createSQLQuery(sql + limit_sql), objects).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
            pagination.setTotal(total);
            pagination.setRows(rows == null ? Lists.newArrayList():rows);
            return pagination;
        }catch(Exception e){
            LOGGER.error(String.format("query sql [%s]  for list error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public boolean executeSQLUpdate(String sql, Object [] objects) {
        Session session = this.sessionFactory.openSession();
        try {
            Transaction tx = session.beginTransaction();
            SQLQuery q = bindSQLQueryParams(session.createSQLQuery(sql), objects);
            tx.commit();
            return q.executeUpdate() >= 0 ;
        }catch(Exception e){
            LOGGER.error(String.format("execute [%s]  error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return false;
    }


    public<T> T get(Serializable id, Class<T> clazz) {
        Session session = this.sessionFactory.openSession();
        try {
            return session.get(clazz, Objects.requireNonNull(id));
        }catch(Exception e){
            LOGGER.error(String.format("get %s [%s] error", clazz, id), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    /**
     * 持久化并返回实体类
     * @param entity
     * @param <T>
     * @return
     * @throws HibernateException
     */
    public<T> T persist(T entity) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            Transaction tx = session.beginTransaction();
            session.saveOrUpdate(Objects.requireNonNull(entity));
            tx.commit();
            return entity;
        }catch(Exception e){
            LOGGER.error(String.format("saveOrUpdate [%s]  error", new Gson().toJson(entity).toString()), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public<T> T delete(T entity) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            Transaction tx = session.beginTransaction();
            session.delete(Objects.requireNonNull(entity));
            tx.commit();
            return entity;
        }catch(Exception e){
            LOGGER.error(String.format("delete [%s]  error", new Gson().toJson(entity).toString()), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }

    public<T> boolean deleteBentch(List<T> entitys) throws HibernateException {
        Session session = this.sessionFactory.openSession();
        try {
            Transaction tx = session.beginTransaction();
            int i = 0;
            for(T entity:entitys){
                session.delete(Objects.requireNonNull(entity));
                i++;
                if( i%30 == 0){
                    session.flush();
                    session.clear();
                }
            }
            tx.commit();
            return true;
        }catch(Exception e){
            LOGGER.error(String.format("delete [%s]  error", new Gson().toJson(entitys).toString()), e);
        }finally {
            if(session != null)
                session.close();
        }
        return false;
    }

    public <T> boolean addBentch(List<T> entitys) {
        Session session = this.sessionFactory.openSession();
        try {
            Transaction tx = session.beginTransaction();
            int i = 0;
            for(T entity:entitys){
                session.saveOrUpdate(Objects.requireNonNull(entity));
                i++;
                if( i%30 == 0){
                    session.flush();
                    session.clear();
                }
            }
            tx.commit();
            return true;
        }catch(Exception e){
            LOGGER.error(String.format("saveOrUpdate [%s]  error", new Gson().toJson(entitys).toString()), e);
        }finally {
            if(session != null)
                session.close();
        }
        return false;
    }
    public Object uniqueResult(String sql,Object [] objects) {
        Session session = this.sessionFactory.openSession();
        try {
            List<Object> results = bindSQLQueryParams(session.createSQLQuery(sql),objects).list();
            if(results !=null && results.size()>0){
                return results.get(0);
            }
        }catch(Exception e){
            LOGGER.error(String.format("query  [%s]error", sql), e);
        }finally {
            if(session != null)
                session.close();
        }
        return null;
    }
}
